﻿Imports System.ServiceModel

Public Class pscBrain
    Private WithEvents Timer As New System.Timers.Timer(6000)
    Friend Manager As New CronManager
    Private ServiceHost As ServiceHost
    Private _IsServiceRunning As Boolean
    Public Shared ReadOnly theBrain As New pscBrain
    Public ReadOnly Property IsServiceRunning As Boolean
        Get
            Return Me._IsServiceRunning
        End Get
    End Property

    Private Sub New()
    End Sub

    Private Sub Timer_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) Handles Timer.Elapsed
        If Me.Manager.CronCount > 0 Then Console.WriteLine("Jobs currently running: {0}.", Me.Manager.CronCount.ToString)
        'Call ExecuteJobs()
    End Sub

    Private Sub ExecuteJobs()
        Using db As New PowerShellCronContext
            For Each j As Job In db.Jobs.Where(Function(x) x.NextRun <= DateTimeOffset.Now).ToList
                Me.Manager.Consume(j)
                Try
                    j.UpdateNextRun()
                Catch ex As Exception
                    Log.LogMessage(String.Format("Failed to call UpdateNextRun on Job {0}:{1}", j.Name, ex.ToString), LogTypes.Error)
                    j.NextRun = Nothing
                End Try
            Next
            db.SaveChanges()
        End Using
    End Sub

    Private Sub StartEndPoint()
        'New Code
        Me.ServiceHost = New ServiceHost(GetType(ManageService))

        'ServiceMetaData Behavior
        'Dim smb As New Description.ServiceMetadataBehavior
        'Me.ServiceHost.Description.Behaviors.Add(smb)

        ''HTTP
        'If System.Configuration.ConfigurationManager.AppSettings.AllKeys.Contains("HttpURI") Then
        '    Dim httpURI As String = System.Configuration.ConfigurationManager.AppSettings.Item("HttpURI")
        '    Dim hBinding As New BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly)
        '    hBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows

        '    Me.ServiceHost.AddServiceEndpoint(GetType(ServiceContract.IManagePSC), hBinding, httpURI)

        '    smb.HttpGetEnabled = True
        '    smb.MetadataExporter.PolicyVersion = Description.PolicyVersion.Policy15
        '    smb.HttpGetUrl = New Uri(httpURI)
        'End If

        'NET-TCP
        Dim tcpPort As Integer = 9000
        If System.Configuration.ConfigurationManager.AppSettings.AllKeys.Contains("ManagementPort") Then
            tcpPort = System.Configuration.ConfigurationManager.AppSettings.Item("ManagementPort")
        End If
        Dim ntBinding As New NetTcpBinding(SecurityMode.Transport)
        ntBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows
        Me.ServiceHost.AddServiceEndpoint(GetType(ServiceContract.IManageSvc), ntBinding, "net.tcp://localhost:" + tcpPort.ToString() + "/PowerShellCron")

        ''MEX
        'Dim mexBinding As ServiceModel.Channels.Binding = ServiceModel.Description.MetadataExchangeBindings.CreateMexTcpBinding
        'Me.ServiceHost.AddServiceEndpoint(GetType(ServiceModel.Description.IMetadataExchange), mexBinding, "net.tcp://localHost:" + tcpPort.ToString() + "/MEX")

        Me.ServiceHost.Open()
    End Sub

    Public Sub ServiceStart()
        Dim sw As New Stopwatch
        sw.Start()
        Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of PowerShellCronContext, Migrations.Configuration))
        Using psc As New PowerShellCronContext
            psc.Database.Initialize(False)
        End Using
        sw.Stop()

        Log.LogMessage(String.Format("Database Initialization took {0} milleseconds.", sw.ElapsedMilliseconds), LogTypes.Information)
        Log.LogMessage("PowerShell Cron Service is starting.")
        StartEndPoint()

        'ExecuteJobs()
        Timer.Start()

        Log.LogMessage("PowerShell Cron Service is started.")
        Me._IsServiceRunning = True
    End Sub

    Public Sub ServiceStop()
        Call Log.LogMessage("PowerShell Cron Service is stopping.")

        If Me.ServiceHost IsNot Nothing Then
            Me.ServiceHost.Close()
            Me.ServiceHost = Nothing
        End If

        Timer.Stop()
        Dim forcedShutdown As Boolean = False

        If Manager.CronCount > 0 And Not forcedShutdown Then
            Call Log.LogMessage(String.Format("PowerShellCron is shutting down with {0} jobs running.", Manager.CronCount.ToString()), LogTypes.Warning)

            Manager.CancelAll()

            Dim c As Integer = 1
            While Manager.CronCount > 0
                If c > 40 Then
                    Call Log.LogMessage(String.Format("{0} seconds have elapsed and {1} job(s) are still running, closing down service anyways.", (0.25 * (c - 1)).ToString(), Manager.CronCount.ToString()), LogTypes.Warning)
                    forcedShutdown = True

                    For Each ct As iCronTask In Manager.CronList
                        Call Log.LogMessage(String.Format("Job '{0}' still running.", ct.CronName))
                        'ct.
                    Next
                    Exit While
                End If
                c += 1
                Threading.Thread.Sleep(250)
            End While
        End If

        Call Log.LogMessage("PowerShell Cron Service is stopped.")
        Me._IsServiceRunning = False
    End Sub

#Region "OtherNotPermanent"

    'Public Sub ResetDatabase()
    '    Using psc As New Service.PowerShellCronContext
    '        If Not psc.Database.Exists Then
    '            psc.Database.Create()
    '        Else
    '            psc.Database.Delete()
    '            psc.Database.Create()

    '            psc.Jobs.Add(New Job With {.Name = "PSTest",
    '                                       .Interval = 1,
    '                                       .IntervalType = IntervalTypes.Minute,
    '                                       .ForceDebug = True,
    '                                       .ForceVerbose = True,
    '                                       .Script = "Write-Host 'Starting PSTest..'; write-host 'Write-host'; Write-Verbose 'Write-Verbose'; Write-Warning 'write-warning'; write-debug 'write-debug'; Write-Error 'write-error'; Write-Output 'write-output'; [console]::writeline('console::writeline'); Write-Host 'PS Test ending..'"})

    '            psc.Jobs.Add(New Job With {.Name = "PSTest2",
    '                                       .Interval = 1,
    '                                       .IntervalType = IntervalTypes.Minute,
    '                                       .ForceDebug = True,
    '                                       .ForceVerbose = True,
    '                                       .Script = "(1..12) | % {Write-Host ('Length: {0}' -f [math]::Pow(2,$_)); Write-Host (Get-RandomPassword -Length ([math]::Pow(2,$_)))}"})

    '            psc.Jobs.Add(New Job With {.Name = "PS Wait Test (10seconds)",
    '                                       .Interval = 1,
    '                                       .IntervalType = IntervalTypes.Minute,
    '                                       .ForceDebug = True,
    '                                       .ForceVerbose = True,
    '                                       .Script = "Write-Host 'Beginning Wait Test...'; start-sleep -seconds 10; write-host 'Finished Wait Test!'"})

    '            Dim sb As New Text.StringBuilder
    '            sb.AppendLine("Param([string]$name, [int]$age)")
    '            sb.AppendLine("Write-Host ('Hello World! says {0}.' -f $name)")
    '            sb.AppendLine("Write-Host ('{0} is {1}' -f $name, $age)")
    '            sb.AppendLine("Write-Host 'Finished!'")

    '            psc.Jobs.Add(New Job With {.Name = "PSParmTest",
    '                                                       .Interval = 1,
    '                                                       .IntervalType = IntervalTypes.Minute,
    '                                                       .ForceDebug = True,
    '                                                       .ForceVerbose = True,
    '                                                       .Script = sb.ToString()})

    '            psc.Jobs.Add(New Job With {.Name = "ModuleTest",
    '                                       .Interval = 1,
    '                                       .IntervalType = IntervalTypes.Minute,
    '                                       .ForceDebug = False,
    '                                       .ForceVerbose = False,
    '                                       .AllowMultiple = True,
    '                                       .Script = "Import-Module \\sbcsystems.local\it\powershellmodules\pstools;Write-Host 'Getting Current Modules...'; Get-Module; Write-Host 'Getting All Modules...'; Get-Module -ListAvailable; $profile | select *; gci env:"})

    '            psc.SaveChanges()
    '        End If
    '    End Using
    'End Sub

    Public Sub CalcNextTime()
        Dim theScript As String = "write-error 'hi' -EA stop;$_.addyears($_.hour).addmonths($_.minute).adddays($_.second)"
        Dim currentDTO As DateTimeOffset = DateTimeOffset.Now

        Console.WriteLine(String.Format("Current DTO: {0}", currentDTO.ToString))
        Console.WriteLine(String.Format("Values to Add: Year={0}; Month={1}; Day={2}", currentDTO.Hour, currentDTO.Minute, currentDTO.Second))
        Console.WriteLine(New String("=", 50))

        Try
            Console.WriteLine(String.Format("Calc DTO: {0}", PSExecuter.PSExecute.Caculate(Of DateTimeOffset, DateTimeOffset)(theScript, currentDTO)))
        Catch
        End Try
        Console.WriteLine(New String("=", 50))
        Console.WriteLine("Source: 10; Check: 2; Result: {0}", 10 And 2)
        Console.WriteLine("Source: 10; Check: 4; Result: {0}", 10 And 4)
        Console.WriteLine("Source: 10; Check: 8; Result: {0}", 10 And 8)
    End Sub
    Public Sub RunListener()
        Dim tj As DataContract.Job
        Dim oj As Job
        Using db As New PowerShellCronContext
            oj = db.Jobs.Find(2)
            tj = Mapper.ToJob(oj)
        End Using


        With tj
            .Steps.Add(New DataContract.JobStep With {.StepNumber = 3, .ScriptId = 1, .JobId = tj.JobId})
        End With

        Using db As New PowerShellCronContext
            Dim j As Job = Mapper.FromJob(tj)
            db.Jobs.Attach(j)
            db.Entry(j).State = EntityState.Modified
            db.SaveChanges()
        End Using

        Exit Sub

        Using db As New PowerShellCronContext
            Dim s As New Script With {.Name = "TestScript",
                                        .SingleInstance = False,
                                        .Code = "param([string]$name) write-host ('Hello World! ~{0}' -f $name)",
                                        .Description = "Hello World"}

            Dim j As New Job With {.Name = "TestJob"}
            Dim js As New JobStep With {.Script = s, .StepNumber = 1}
            js.Parameters.Add("name", "Robert")

            j.Steps.Add(js)
            db.Jobs.Add(j)
            db.SaveChanges()

        End Using
        'Dim myListener As New PSInterpeter.CustomHostListener
        'myListener.Run()
    End Sub
#End Region
End Class